/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package recognizer.neuron;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

/**
 * Нейронная сеть
 * @author Кирилл Чикалин
 */
public class NeuronNetwork {
   /**
    * Количество входных сигналов
    */
   private int numberOfInputs = 0;
   /**
    * Таблица нейронов (имя - нейрон)
    */
   private HashMap<String, Neuron> neurons = new HashMap<String, Neuron>();
   /**
    * Конструктор
    * @param numberOfInputs Количество входных сигналов
    * @param numberOfNeurons Количество нейронов
    */
   public NeuronNetwork(int numberOfInputs, ArrayList<String> neuronNames) {
       
       this.numberOfInputs = numberOfInputs;
       // Создаём заданное количество нейронов со случайными значениями
       Random random = new Random();
       for(String name : neuronNames) {
           neurons.put(name, new Neuron(numberOfInputs, name));
       }
    }
   
   /**
    * 
    * @param facts 
    */
   public void training(HashMap<boolean[], String> facts){
       // Проверяем/создаём нейроны для примеров
       for (String name : facts.values()) {
           // Если нет нейрона для данного факта
           if (!neurons.keySet().contains(name)) {
               neurons.put(name, new Neuron(numberOfInputs, name));
           }
       }
       // Ошибка
       double s_err;
       // Погрешность
       double eps = 0.5d;
       
       double speedTraining = 0.1;
       
       int e;
       // Цикл обучения
       do {
           s_err = 0.0d;
           // Цикл обхода всех примеров обучения
           for (boolean[] inputFactSignals : facts.keySet()) {
               // Цикл обхода всех нейронов
               for (Neuron neuron : neurons.values()) {
                   // Передаём входные сигналы
                   neuron.setInputSignals(inputFactSignals);
                   
                   // Определяем ошибки
                   int neuronState = neuron.getState() ? 1 : 0;
                   int d = neuron.getName().equals(facts.get(inputFactSignals)) ? 1 : 0;
                   e = neuronState - d;
                   s_err += e*e;
                   
                   // Устанавливаем новые значения весовых коэффициентов
                   for (int i = 0; i < neuron.getWeights().length; i++) {
                       neuron.setWeight(i, 
                               neuron.getWeight(i) 
                               - speedTraining * e * (inputFactSignals[i] ? 1 : 0));
                   }
                   
                   neuron.setThreshold(neuron.getThreshold() + speedTraining * e);
               }
           }
           System.out.println("Ошибка " + s_err);
       } while (s_err > eps);
   }

   /**
    * Распознание
    * @param signals Входные сигналы
    * @return Таблица совпадения
    */
   public void recognize(boolean[] signals)
   {
       // Передаём входные сигналы каждому нейрону
       for(Neuron neuron : neurons.values()) {
           neuron.setInputSignals(signals);
           neuron.getState();
           //results.put(neuron.getName(), neuron.getWeighedSum() - neuron.getThreshold());
           System.out.println(neuron.getName() + " статус " + neuron.getState());
       }
   }

   /**
    * Возвращает Список нейронов
    * @return Список нейронов
    */
   public Object[] getNeurons() {
       return  neurons.values().toArray();
   }
}